<?php
/*
Plugin Name: WP Smart Sort
Plugin URI: http://dyasonhat.com/wp-smart-sort/
Description: Advanced posts sorting for wordpress. Sort by various fields including custom fields.
Author: dyasonhat
Version: 2.0.1
Author URI: http://dyasonhat.com/
*/
     
class WP_Smart_Sort {
    

    function ss_init() {
	    global $wp_rewrite;
	    
   	    $wp_rewrite->flush_rules();
	    
    }
    
    function ss_rewrite($wp_rewrite) {
	    /*$keywords_structure, 
                $ep_mask = EP_NONE, 
                $page = true, 
                $feed = true, 
                $forcomments = false, 
                $walk_dirs = true); */
        /*
        * SORT REWRITE RULES
        */
        $sort_token = '%ssort%';
        $wp_rewrite->add_rewrite_tag($sort_token, '(.+?)', "ssort=");
        
        $keywords_structure = array();
        //index
        $keywords_structure[] = $wp_rewrite->root . "/";
        //categories
        $keywords_structure[] = $wp_rewrite->category_structure . "/";
        //tag
        $keywords_structure[] = $wp_rewrite->tag_structure . "/";
        //author
        $keywords_structure[] = $wp_rewrite->author_structure . "/";
        //date
        //year
        $keywords_structure[] = $wp_rewrite->get_year_permastruct() . "";
        //month
        $keywords_structure[] = $wp_rewrite->get_month_permastruct() . "";
        //day
        $keywords_structure[] = $wp_rewrite->get_day_permastruct() . "/";
            
        foreach ($keywords_structure as $keyword_structure) {
            $wp_rewrite->rules = $wp_rewrite->generate_rewrite_rules($keyword_structure . "sort/$sort_token", EP_NONE, true, true, false, false) + $wp_rewrite->rules;
        }

    }

    function ss_insert_my_rewrite_query_vars($vars) {
      $vars[] = 'ssort';
      return $vars;
    }
    
    function ss_get_term_id_by_slug ($slug, $taxonomy) {
        global $wpdb;
        
        
        return $id;
    }
    
    function ss_get_current_context_url(){
        global $wp_query;
        global $wpdb;

        $url = '';
        if (is_category()) {
            $cat_id = $wp_query->get('cat');
            $url = get_category_link($cat_id);
        }
        elseif (is_tag()) {
           $tag_name = $wp_query->get('tag');
           $sql = "SELECT ".$wpdb->terms.".erm_id FROM $wpdb->term_taxonomy
                                    LEFT JOIN $wpdb->terms
                                    ON (".$wpdb->term_taxonomy.".term_id = ".$wpdb->terms.".term_id)
                                    WHERE ".$wpdb->terms.".slug = '$tag_name'
                                    AND ".$wpdb->term_taxonomy.".taxonomy = 'post_tag'
                                    LIMIT 1";
           $tag_id = $wpdb->get_var($sql);
           $url = get_tag_link($tag_id);
        }
        elseif (is_author()) {
            $author_id = $wp_query->get('author');
            $url = get_author_posts_url($author_id);
        }
        elseif (is_date()) {
            $year = $wp_query->get('year');
            $month = $wp_query->get('monthnum');
            $day = $wp_query->get('day');
            
            if ($wp_query->get('day')) {
                $url = get_day_link( $year,  $month,  $day);
            }
            elseif ($wp_query->get('monthnum')) {
                $url = get_month_link( $year,  $month,  $day);
            }
            elseif ($wp_query->get('year')) {
                $url = get_year_link( $year,  $month,  $day);
            }
        }
        else {
            $url = get_bloginfo('url');
        }
        
        //ensure trailing slash
        if ("/" != substr($url, -1)) {
            $url = $url . "/";
        }
        return $url;
    }
    
    function ss_widget_sort ($args){
        extract($args);
        ?>
        <?php echo $before_widget; ?>
          <?php echo $before_title
              . 'Sort By'
              . $after_title; ?>
          <?php $this->placesort(); ?>
        <?php echo $after_widget;
    }
    
    function ss_plugins_loaded () {
        register_sidebar_widget("Smart Sort",array($this, "ss_widget_sort")); 
    }
    
    function placesort() {

	    $sort_drop_title = get_option('ss_sort_drop_down_title');
        $saved_options = $this->ss_get_options();
        $ssort = $this->ss_open_ssort();
	    $sorthtml = '<select name="ssort" onChange="document.location.href=this.options[this.selectedIndex].value;">';
        
        foreach ($saved_options as $option) {
            $selected = '';
            if ($option['nicekey']."-asc" == $ssort['sort'] . "-" . strtolower($ssort['dir'])) { 
                $selected = "selected"; 
            }
            $sorthtml .= "<option value='".$option['asclink']."' $selected onClick='window.location = \"".$option['asclink']."\"; return false;' >".$option['title']." ". __('ASC')."</option>";
            
            $selected = '';
            if ($option['nicekey']."-desc" == $ssort['sort'] . "-" . strtolower($ssort['dir'])) { 
                $selected = "selected";
            }
            $sorthtml .= "<option value='".$option['desclink']."' $selected onClick='window.location = \"".$option['desclink']."\"; return false;' >".$option['title']." ". __('DESC')."</option>";
        }
	    $sorthtml .= '</select>';
        
	    echo "$sorthtml";
    }
    
    function ss_add_pages() {
        // Add a new menu under Options:
        add_options_page('WP Smart Sort', 'WP Smart Sort', 8, __FILE__, array($this, 'ss_options_sort_page'));
    }

    function ss_orderby($sort) {

        $ssort = $this->ss_open_ssort($sortvar);
        
	    $sort   = $ssort['sortfield'];
        if ($ssort['isnumerical']) {
            $sort = "(" . $sort . "+0)";
        }
        $dir    = $ssort['dir'];

	    return " $sort $dir ";
    }

    function ss_join($jn){
	    global $wpdb;
	    
	    $ssort = $this->ss_open_ssort();    
        
        if ($ssort['ismeta']) {
            return $jn . "LEFT JOIN $wpdb->postmeta ON ($wpdb->posts".".ID = $wpdb->postmeta".".post_id)";
        }
        
        return $jn;
    }
    /*
    * Returns an array containing all the saved sort options
    */
    function ss_get_options () {
        
        if (get_option("ss_sort_item")) {
            $compound_options = get_option("ss_sort_item");
            $semi_compound_options = explode("---", $compound_options);
            
            $ss_options = array();
            
            foreach ($semi_compound_options as $semi_o) {
                $option['key'] = "";
                $option['nicekey'] = "";
                $option['numerical'] = "";
                $option['title'] = "";
                
                $compound_key = substr($semi_o,0, strpos($semi_o,"{"));
                
                $compound_key = explode("::", $compound_key);
                $option['nicekey'] = array_pop($compound_key);
                $option['key'] = implode("::", $compound_key);
                $key = $option['nicekey'];
                        
                $key_values = substr($semi_o, (strpos($semi_o,"{")+1), (strrpos($semi_o,"}") - strpos($semi_o,"{")-1) );
                $key_values = explode("::", $key_values);
                
                $option['numerical'] = array_pop($key_values);
                $option['title'] = implode("::", $key_values);
                
                $option['asclink'] = $this->ss_get_current_context_url() . 'sort/' . $option['nicekey'] . '-asc/';
                $option['desclink'] = $this->ss_get_current_context_url() . 'sort/' . $option['nicekey'] . '-desc/';
        
                $ss_options["$key"] = $option;
            }
        }
        else {
            add_option("ss_sort_item","");
            $ss_options = array();        
        }
            
        return $ss_options;
    }
    
    function ss_open_ssort () {
        global $wpdb;
        global $wp_query;
        
        if ($wp_query->get('ssort')) {
           $sortvar = $wp_query->get('ssort');
        }
        else {
            $sortvar = get_option('ss_default_sort_direction');
        }
        
        $str1 = substr($sortvar, strlen($sortvar)-4, 4);
        $str2 = substr($sortvar, strlen($sortvar)-5, 5);
        if ($str1 == '-asc') {
            $dir = "ASC";
            $sortby = substr($sortvar, 0, strlen($sortvar)-4);
        } elseif ($str2 == "-desc") { //ensures the ASC is at the end of the string which means ASC and not found in the string elsewhere eg sort=nascar-desc
            $dir = "DESC";
            $sortby = substr($sortvar, 0, strlen($sortvar)-5);
        }
        else {
            $dir = false;
            $sortby = $sortvar;
        }
        
        $ssort['dir'] = $dir;
        $ssort['sort'] = $sortby;
        $ssort['isnumerical'] = false;
        
        $field_type = substr($sortby,-3);
        $saved_options = $this->ss_get_options();
        if ($field_type == "-pm") { // its a meta sort
            $metatable = $wpdb->postmeta;
            $cf_name = $saved_options["$sortby"]['key'];
            $ssort['nicekey'] = $saved_options["$sortby"]['nicekey'];
            $ssort['fieldkey'] = substr($cf_name, strlen($metatable)+1, strlen($cf_name) - strlen($metatable) - 1);
            $ssort['sortfield'] = $metatable . ".meta_value";
            $ssort['ismeta'] = true;
            if ($saved_options["$sortby"]['numerical'] == '1') {
                $ssort['isnumerical'] = true;
            }
        }
        elseif ($field_type == "-pp") { // its a post table sort
            $posttable = $wpdb->posts;
            $cf_name = $saved_options["$sortby"]['key'];
            $ssort['nicekey'] = $saved_options["$sortby"]['nicekey'];
            $ssort['fieldkey'] = substr($cf_name, strlen($posttable)+1, strlen($cf_name) - strlen($posttable) - 1);
            $ssort['sortfield'] = $posttable . "." . $ssort['fieldkey'];
            $ssort['ismeta'] = false;
            if ($saved_options["$sortby"]['numerical'] == '1') {
                $ssort['isnumerical'] = true;
            }
        }
        else {
            $ssort['sortfield'] = $sortby;
            $ssort['ismeta'] = false;
        }
        
        if (!$ssort['sortfield']) {$ssort['sortfield'] = "post_date"; }
        if (!$ssort['dir']) {$ssort['dir'] = "DESC"; }
        if (!$ssort['ismeta']) {$ssort['ismeta'] = false; }
        
        return $ssort;
        
    }
    
    function ss_make_nice_key ($key) {
        global $wpdb;
        $metatable = $wpdb->prefix . "postmeta";
        $posttable = $wpdb->prefix . "posts";
        
        if (substr($key, 0, strlen($metatable)) == $metatable) { // is a meta table key
            $field = substr($key, strlen($metatable), strlen($key) - strlen($metatable) );
            $field = ereg_replace("[^A-Za-z0-9]", "_", $field );
            $new_key = $field . "-pm";
        }
        elseif (substr($key, 0, strlen($posttable)) == $posttable) { //is a post table key
            $field = substr($key, strlen($posttable), strlen($key) - strlen($posttable) );
            $field = ereg_replace("[^A-Za-z0-9]", "_", $field );
            $new_key = $field . "-pp";    
        }
        else { //dunno what it is
            $new_key = $key;
        } 
        
        return $new_key;
    }
    /*
    * Produces a single compounded option
    */
    function ss_make_option ($key, $title, $numerical) {
        $option = $key . "::" . $this->ss_make_nice_key($key) . "{" . $title . "::" . $numerical . "}";
        return $option;
    }

    /*
    * Adds an option to the sort options list
    */
    function ss_add_option ($option) {
        
        if (get_option("ss_sort_item") !== false){
            $current = get_option("ss_sort_item");
            if ($current != '') {
                $new = $current . "---" . $option;
            }
            else {
                $new = $option;
            }
            
            update_option("ss_sort_item", $new);
        }
        else {
            add_option("ss_sort_item", $option);
        }
        
    }
    /*
    * Deletes an option from the sort options list
    */
    function ss_delete_option ($nicekey) {
        
        $saved_options = $this->ss_get_options();
        $newitems = array();
        
        foreach($saved_options as $option){
            if ($option['nicekey'] != $nicekey){
                $newitems[] = $this->ss_make_option($option['key'],$option['title'],$option['numerical']);
            }
        }
        $this->ss_save_total_options($newitems);
        
    }
    /*
    * Saves the sort options to wp_options
    * @param options is an array.
    */
    function ss_save_total_options ($options) {
        
        $total_options = implode("---",$options);
        
        if (get_option("ss_sort_item")){
            update_option("ss_sort_item", $total_options);
        }
        else {
            add_option("ss_sort_item", $total_options);
        }
        
    }
    
    function ss_update_option($sortkey, $newoption) {

        $saved_options = $this->ss_get_options();
        $newitems = array();
        
        foreach($saved_options as $option){
            if ($option['key'] == $sortkey){
                $newitems[] = $newoption;
            }
            else {
                $newitems[] =$this-> ss_make_option($option['key'],$option['title'],$option['numerical']);
            }
        }
        ss_save_total_options($newitems);
        
    }
    
    //OPTIONS Sort Page
    function ss_options_sort_page() {
        global $_POST;
        global $_POST;
        global $wpdb;
        
        
        $saved_options = $this->ss_get_options();
         
        if ($_POST['action'] == 'add'){
            if ($_POST['sortkey'] and $_POST['sorttitle']){
                
                $option = $this->ss_make_option($_POST['sortkey'],$_POST['sorttitle'],$_POST['numerical']);
                $this->ss_add_option ($option);
                
            }
        } else if ($_POST['action'] == 'delete'){
            if ($_POST['sortkey']){
                
                $this->ss_delete_option($this->ss_make_nice_key($_POST['sortkey']));
                
            }
        } else if ($_POST['action'] == 'update'){
            if ($_POST['sortkey']){
                
                $option =  $this->ss_make_option($_POST['sortkey'],$_POST['sorttitle'],$_POST['numerical']);
                $this->ss_update_option($_POST['sortkey'], $option);
            }
        }
        
        /*
        * Get the available fields from "CUSTOM FIELDS"
        */
        $saved_options = $this->ss_get_options();
        $res    =   $wpdb->get_results("SELECT meta_key FROM $wpdb->postmeta GROUP BY meta_key");
        $meta_fields_html = ''; 
        foreach ($res as $des ) {
            
            $key = $wpdb->prefix."postmeta." . $des->meta_key;
            $nicekey = $this->ss_make_nice_key($key);
            $html =    '<table class="form-table">';
            $html .=   '<tr valign="top">
                        <th scope="row">' . $key . '</th>';
            $html .=   '<td>';
            
            if ( array_key_exists($nicekey, $saved_options) ) {
                
                $html .='
                        <form method="post" action="">
                            <div style="float:right">
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="delete" />                            
                                <input type="submit" class="button"  name="Submit" value="'. __('Remove') .'" />
                            </div>
                        </form>
                        <form method="post" action="">
                            <div style="float:right">
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="update" />
                                <input type="submit" class="button"  name="Submit" value="'. __('Update') .'" />
                            </div>
                        ';
            } else {
                $html .='<form method="post" action="">
                            <div style="float:right">                    
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="add" />
                                <input type="submit" class="button" name="Submit" value="'. __('Add') .'" />
                            </div>
                        ';
            }
            
            
            $html .='<div>';
            $html .='<div>
                     <label for="sorttitle" />'. __('Display Text').'</label>
                     <input type="text" name="sorttitle" value="'.$saved_options[$nicekey]['title'].'" />
                     </div>';
            
            $checked = '';
            if ($saved_options[$nicekey]['numerical'] == '1') {
                $checked = 'checked="checked"';
            }
            
            $html .='<div>
                     <label for="numerical" />'. __('Is Numeric').'</label>
                     <input type="checkbox" name="numerical" value="1" '.$checked.'/>
                     </div>';
                     
            $html .='</div></form></td></tr></table>';    
            $meta_fields_html .= $html;
        }
        
        /*
        * Get the available fields from "Posts Table"
        */
        $res    =   $wpdb->get_results("DESCRIBE $wpdb->posts");    
        $posts_fields_html = ''; 
        foreach ($res as $des ) {
            
            $key = $wpdb->prefix."posts." . $des->Field;
            $nicekey = $this->ss_make_nice_key($key);
            $html =    '<table class="form-table">';
            $html .=   '<tr valign="top">
                        <th scope="row">' . $key . '</th>';
            $html .=   '<td>';
            
            if ( array_key_exists($nicekey, $saved_options) ) {
                
                $html .='
                        <form method="post" action="">
                            <div style="float:right">
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="delete" />                            
                                <input type="submit" class="button"  name="Submit" value="'. __('Remove') .'" />
                            </div>
                        </form>
                        <form method="post" action="">
                            <div style="float:right">
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="update" />
                                <input type="submit" class="button"  name="Submit" value="'. __('Update') .'" />
                            </div>
                        ';
            } else {
                $html .='<form method="post" action="">
                            <div style="float:right">                    
                                <input type="hidden" name="sortkey" value="'.$key.'" />
                                <input type="hidden" name="action" value="add" />
                                <input type="submit" class="button" name="Submit" value="'. __('Add') .'" />
                            </div>
                        ';
            }
            
            
            $html .='<div>';
            $html .='<div>
                     <label for="sorttitle" />'. __('Display Text').'</label>
                     <input type="text" name="sorttitle" value="'.$saved_options[$nicekey]['title'].'" />
                     </div>';
            
            $checked = '';
            if ($saved_options[$nicekey]['numerical'] == '1') {
                $checked = 'checked="checked"';
            }
            
            $html .='<div>
                     <label for="numerical" />'. __('Is Numeric').'</label>
                     <input type="checkbox" name="numerical" value="1" '. $checked .'/>
                     </div>';
                     
            $html .='</div></form></td></tr></table>';    
            $posts_fields_html .= $html;
        }
        
        ?><div class='wrap'>
                <h2>WP Smart Sort</h2>
                <P align=center>
                    <h3><?php _e('From Custom Fields'); ?></h3>
                        <?php echo $meta_fields_html; ?>
                    <h3><?php _e('From WP Posts Table'); ?></h3>
                        <?php echo $posts_fields_html; ?>
                    <h3><?php _e('Other Options'); ?></h3>
                        <form method="post" action="options.php">
                            <?php wp_nonce_field('update-options'); ?>
                            <table class="form-table">
                                <tr valign="top">
                                    <th scope="row"><?php _e('Default Sort Direction'); ?></th>
                                    <td><?php 
                                            $opt_fields = $this->ss_get_options(); 
                                            $dsort = "<select name='ss_default_sort_direction'>";
                                            
                                            foreach ($opt_fields as $field) {
                                                $nicekey = $field['nicekey'];
                                                $title = $field['title'];
                                                if ($nicekey . "-asc" == get_option('ss_default_sort_direction')) {
                                                    $sel1 = "selected";
                                                    $sel2 = "";
                                                }
                                                elseif ($nicekey . "-desc" == get_option('ss_default_sort_direction')) {
                                                    $sel1 = "";
                                                    $sel2 = "selected";
                                                }
                                                else {
                                                    $sel1 = "";
                                                    $sel2 = "";
                                                }
                                            
                                                $dsort .= "<option value='$nicekey-asc' $sel1 >$title ($nicekey) " . __('ASC'). "</option>";
                                                $dsort .= "<option value='$nicekey-desc' $sel2 >$title ($nicekey) ". __('DESC'). "</option>";
                                            }
                                            $dsort .= "</select>";
                                            echo $dsort;
                                        ?></td>
                                </tr>
                            </table>

                            <input type="hidden" name="action" value="update" />
                            <input type="hidden" name="page_options" value="ss_default_sort_direction" />

                            <p class="submit">
                                <input type="submit" name="Submit" value="<?php _e('Save Changes') ?>" />
                            </p>

                        </form>
                </p>
            </div><?php 
    }

    function ss_where($where){
        global $wpdb, $wp_query;
        
        $ssort = $this->ss_open_ssort();    
        
        if ($ssort['ismeta']) {
            $where = $where ." AND ". $wpdb->postmeta.".meta_key = '".$ssort['fieldkey']."' ";
        }

        return $where;
    }
}


/*
ALL the ACTIONS / FILTERS / HOOKS
*/

$wpss = new WP_Smart_Sort();

add_action('admin_menu',    array($wpss, 'ss_add_pages'));
add_filter('posts_orderby', array($wpss, 'ss_orderby'));
add_filter('posts_where',   array($wpss, 'ss_where'));
add_filter('posts_join',    array($wpss, 'ss_join'));
add_action('init',          array($wpss, 'ss_init'));

add_action('plugins_loaded',         array($wpss, 'ss_plugins_loaded'));
add_filter('query_vars',             array($wpss, 'ss_insert_my_rewrite_query_vars'));
add_filter('generate_rewrite_rules', array($wpss, 'ss_rewrite'));

?>